/*
Copyright 2011 Gabriel Flores

*/
package com.asfusion.mate.extension.testing
{
	import com.asfusion.mate.testing.MockMethod;
	import com.asfusion.mate.testing.MockOperation;
	
	import flash.utils.Dictionary;
	
	import mx.messaging.ChannelSet;
	import mx.messaging.MessageAgent;
	import mx.messaging.messages.IMessage;
	import mx.rpc.AbstractOperation;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.remoting.mxml.RemoteObject;

	[DefaultProperty("methods")]
	[Exclude(name="destination", kind="property")]
	[Exclude(name="channelSet", kind="property")]
	[Exclude(name="concurrency", kind="property")]
	[Exclude(name="endpoint", kind="property")]
	[Exclude(name="makeObjectsBindable", kind="property")]
	[Exclude(name="operations", kind="property")]
	[Exclude(name="requestTimeout", kind="property")]
	[Exclude(name="source", kind="property")]
	[Exclude(name="invoke", kind="event")]
	
	
	public class MockChannelSet extends ChannelSet
	{
		/**
		 * Class to instantiate that will generate the mock result.
		 * This attribute needs to be supplied here or in the individual MockMethod tags. 
		 */
		public var mockGenerator:Class;
		
		/**
		 * Number of seconds to take to return the result or fault after making the call. This helps making
		 * the illusion that the service is taking time to respond. Default is 0 (no delay). 
		 */
		public var delay:uint = 0;
		
		/**
		 * @todo
		 */
		public var cache:Boolean =  true;
		/**		 * Indicates whether any unhandled errors thrown by the service method should be caught		 * and dispatched as a fault. Default is true.		 */		public var useFault:Boolean = true;
		public var showBusyCursor:Boolean = true;		/**
		 * @todo
		 */
		protected var methodsDictionary:Dictionary;
		
		// -------------------------------
		private var _methods:Array;
		/**
		 * @todo
		 */
		public function get methods():Array
		{
			return _methods;
		}
		
		[ArrayElementType("com.asfusion.mate.testing.MockMethod")]
		public function set methods(value:Array):void
		{
			_methods = value;
			
			for each (var method:MockMethod in value) {
				if (method.delay == -1) {
					//set parent's default delay
					method.delay = delay;
				}
				if (method.mockGeneratorMethod == null) {
					method.mockGeneratorMethod = method.name;
				}
				methodsDictionary[method.name] = method;
			}
		}
		
		private var _authenticated:Boolean = false;
		
		override public function get authenticated():Boolean
		{
			return _authenticated;
		}
		
		private var loginInProgress:Boolean = false;
		private var logoutInProgress:Boolean = false;
		
		//--------------------------------------------------------------------------
	    // Contructor
	    //--------------------------------------------------------------------------
	    /**
	    * Contructor
	    */
		public function MockChannelSet()
		{
			super();
			methodsDictionary = new Dictionary();
		}
			
		// -------------------------------
		/**
		 * @todo
		 */
		public function getOperation(name:String):AbstractOperation
	    {   
	    	var operation:MockOperation = new MockOperation(name, getMethod(name), showBusyCursor, useFault);
	    	//add fault and result listeners
	    	operation.addEventListener(ResultEvent.RESULT, dispatchResult, false, 0, true);
	    	operation.addEventListener(FaultEvent.FAULT, dispatchFault, false, 0, true);
	        return operation;
	    }

	
		// These two functions dispatch the result and fault events
		// that trigger the inner handlers
		// -------------------------------
		protected function dispatchResult(event:ResultEvent):void 
		{
			if( loginInProgress )
			{
				loginInProgress = false;
				_authenticated = true;
			}
			if( logoutInProgress )
			{
				logoutInProgress = false;
				_authenticated = false;
			}
			dispatchEvent(ResultEvent.createEvent(event.result, event.token));
		}
		
		// -------------------------------
		protected function dispatchFault(event:FaultEvent):void 
		{
			loginInProgress = false;
			logoutInProgress = false;
			dispatchEvent(FaultEvent.createEvent(event.fault, event.token));
		}
		
		
		// -------------------------------
		protected function getMethod(name:String):MockMethod 
		{
			
			if (methodsDictionary[name] == null ) 
			{
				
				var newMethod:MockMethod = new MockMethod();
				newMethod.name = name;
				newMethod.delay = delay;
				newMethod.mockGeneratorMethod = name;
				newMethod.mockGenerator = mockGenerator;
				newMethod.cache = cache;
				methodsDictionary[name] = newMethod;
			}
			
			var method:MockMethod = methodsDictionary[name];
			
			//we need to do this here and not in the methods() 
			// setter because it seems that the mockGenerator
			// variables has not been set by then
			if (method.mockGenerator == null) 
			{
				method.mockGenerator = mockGenerator;
			}
			return method;
		}
		
	    override public function login(username:String, password:String, charset:String=null):AsyncToken
		{
	    	var operation:MockOperation = getOperation( "login" ) as MockOperation;
			loginInProgress = true;
			logoutInProgress = false;
			return operation.send( username, password );
		}

		override public function logout(agent:MessageAgent=null):AsyncToken
		{
			var operation:MockOperation = getOperation( "logout" ) as MockOperation;
			loginInProgress = false;
			logoutInProgress = true;
			return operation.send();
		}
		
		override public function send(agent:MessageAgent, message:IMessage):void
		{
			// prevent real sending...
			return;
		}


	}
}